use std::{rc::Rc, cell::RefCell, sync::{Arc, Mutex}, collections::HashSet, time::Duration};

use once_cell::sync::Lazy;

use crate::{rtda::{Frame, heap::{ArrayObject, Class}, DataType, Slots, Object, ExtraType}, instructions::base, native::{self, java::lang::Thread::THREADS}, classfile::ClassFile};

// 存储unpark()执行时，未被park的线程
static UNPARK: Lazy<Arc<Mutex<HashSet<usize>>>> = Lazy::new(|| Arc::new(Mutex::new(HashSet::new())));

static MISC_UNSAFE: &'static str = "sun/misc/Unsafe";
// openjdk8\src\share\classes\sun\misc\Unsafe.java
pub fn init() {
    native::register(MISC_UNSAFE, "arrayBaseOffset", "(Ljava/lang/Class;)I", array_base_offset);
    native::register(MISC_UNSAFE, "arrayIndexScale", "(Ljava/lang/Class;)I", array_index_scale);
    native::register(MISC_UNSAFE, "addressSize", "()I", address_size);
    native::register(MISC_UNSAFE, "objectFieldOffset", "(Ljava/lang/reflect/Field;)J", object_field_offset);
    native::register(MISC_UNSAFE, "compareAndSwapObject", "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z", compare_and_swap_object);
	native::register(MISC_UNSAFE, "getIntVolatile", "(Ljava/lang/Object;J)I", get_int);
	native::register(MISC_UNSAFE, "compareAndSwapInt", "(Ljava/lang/Object;JII)Z", compare_and_swap_int);
	native::register(MISC_UNSAFE, "putObjectVolatile", "(Ljava/lang/Object;JLjava/lang/Object;)V", put_object_volatile);
    native::register(MISC_UNSAFE, "getObjectVolatile", "(Ljava/lang/Object;J)Ljava/lang/Object;", get_object);
	native::register(MISC_UNSAFE, "compareAndSwapLong", "(Ljava/lang/Object;JJJ)Z", compare_and_swap_long);
	native::register(MISC_UNSAFE, "shouldBeInitialized", "(Ljava/lang/Class;)Z", should_be_initialized);
	native::register(MISC_UNSAFE, "ensureClassInitialized", "(Ljava/lang/Class;)V", ensure_class_initialized);

    native::register(MISC_UNSAFE, "defineAnonymousClass", "(Ljava/lang/Class;[B[Ljava/lang/Object;)Ljava/lang/Class;", define_anonymous_class);
    
    native::register(MISC_UNSAFE, "putObject", "(Ljava/lang/Object;JLjava/lang/Object;)V", put_object);
    native::register(MISC_UNSAFE, "putOrderedInt", "(Ljava/lang/Object;JI)V", put_int);



    native::register(MISC_UNSAFE, "unpark", "(Ljava/lang/Object;)V", unpark);
    native::register(MISC_UNSAFE, "park", "(ZJ)V", park);


}

// public native int arrayBaseOffset(Class<?> type);
// (Ljava/lang/Class;)I
fn array_base_offset(frame: Rc<RefCell<Frame>>) {
    let stack = frame.borrow().get_operand_stack();
    stack.borrow_mut().push_int(0); // TODO
}

// public native int arrayIndexScale(Class<?> type);
// (Ljava/lang/Class;)I
fn array_index_scale(frame: Rc<RefCell<Frame>>) {
    let stack = frame.borrow().get_operand_stack();
    stack.borrow_mut().push_int(1); // TODO
}

// public native int addressSize();
// ()I
fn address_size(frame: Rc<RefCell<Frame>>) {
    // let vars = frame.borrow().get_local_vars();
    // vars.borrow().get_ref(0) // this

    let stack = frame.borrow().get_operand_stack();
    stack.borrow_mut().push_int(8); // TODO unsafe.Sizeof(int)
}

// public native long objectFieldOffset(Field field);
// (Ljava/lang/reflect/Field;)J
fn object_field_offset(frame: Rc<RefCell<Frame>>) {
    let vars = frame.borrow().get_local_vars();
    let j_field = vars.borrow().get_ref(1);

    let offset = unsafe { &*j_field.unwrap() }.get_int_var("slot", "I");

    let stack = frame.borrow().get_operand_stack();
    stack.borrow_mut().push_long(offset as i64);
}

// public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x)
// (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z
fn compare_and_swap_object(frame: Rc<RefCell<Frame>>) {
    let vars = frame.borrow().get_local_vars();
    let obj = vars.borrow().get_ref(1);
    let fields = unsafe { &mut *obj.clone().unwrap() }.get_data_mut();
    let offset = vars.borrow().get_long(2);
    let expected = vars.borrow().get_ref(4);
    let new_val = vars.borrow().get_ref(5);

    // TODO
    if let DataType::Slots(anys) = fields {
        // object
        let swapped = _cas_obj(obj.unwrap(), anys, offset, expected, new_val);
        frame.borrow().get_operand_stack().borrow_mut().push_boolean(swapped);
    } else if let DataType::Array(ArrayObject::Object(objs)) = fields {
        // ref[]
        let swapped = _cas_arr(objs, offset, expected, new_val);
        frame.borrow().get_operand_stack().borrow_mut().push_boolean(swapped);
    } else {
        // TODO
        panic!("todo compareAndSwapObject!")
    }
}

fn _cas_obj(obj: *mut Object, field: &mut Slots, offset: i64, expected: Option<*mut Object>, new_val: Option<*mut Object>) -> bool {
    let current = field.get_ref(offset as usize);
    let ptr_eq = match (current, expected) {
        (Some(c), Some(e)) if c == e => true,
        (None, None) => true,
        _ => false
    };
    if ptr_eq {
        field.set_ref(offset as usize, new_val);
        true
    } else {
        false
    }
}

fn _cas_arr(objs: &mut Vec<Option<*mut Object>>,  offset: i64, expected: Option<*mut Object>, new_val: Option<*mut Object>) -> bool {
    let current = objs[offset as usize];
    let ptr_eq = match (current, expected) {
        (Some(c), Some(e)) if c == e => true,
        (None, None) => true,
        _ => false
    };
    if ptr_eq {
        objs[offset as usize] = new_val;
        true
    } else {
        false
    }
}

// public native boolean getInt(Object o, long offset);
// (Ljava/lang/Object;J)I
fn get_int(frame: Rc<RefCell<Frame>>) {
    let vars = frame.borrow().get_local_vars();
    let fields = unsafe { &*vars.borrow().get_ref(1).unwrap() }.get_data();
    let offset = vars.borrow().get_long(2);

    let stack = frame.borrow().get_operand_stack();
    if let DataType::Slots(slots) = fields {
        // object
        stack.borrow_mut().push_int(slots.get_int(offset as usize));
    } else if let DataType::Array(ArrayObject::Int(shorts)) = fields {
        // int[]
        stack.borrow_mut().push_int(shorts[offset as usize]); // 为啥要用shorts作为变量名？？？
    } else {
        panic!("getInt!");
    }
}

// public native void putInt(Object o, long offset, int x);
// (Ljava/lang/Object;JI)V
fn put_int(frame: Rc<RefCell<Frame>>) {
    let vars = frame.borrow().get_local_vars();
    let fields = unsafe { &mut *vars.borrow().get_ref(1).unwrap() }.get_data_mut();
    let offset = vars.borrow().get_long(2) as usize;
    let val = vars.borrow().get_int(4);
    if let DataType::Slots(anys) = fields {
        // object
        anys.set_int(offset, val);
    } else if let DataType::Array(ArrayObject::Int(objs)) = fields {
        // ref[]
        objs[offset] = val;
    } else {
        panic!("putObject!");
    }
}

// public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
// (Ljava/lang/Object;JII)Z
fn compare_and_swap_int(frame: Rc<RefCell<Frame>>) {
    let vars = frame.borrow().get_local_vars();
    let fields = unsafe { &mut *vars.borrow().get_ref(1).unwrap() }.get_data_mut();
    let offset = vars.borrow().get_long(2);
    let expected = vars.borrow().get_int(4);
    let new_val = vars.borrow().get_int(5);

    let stack = frame.borrow().get_operand_stack();
    if let DataType::Slots(slots) = fields {
        // object
        let old_val = slots.get_int(offset as usize);
        if old_val == expected {
            slots.set_int(offset as usize, new_val);
            stack.borrow_mut().push_boolean(true);
        } else {
            stack.borrow_mut().push_boolean(false);
        }
    } else if let DataType::Array(ArrayObject::Int(ints)) = fields {
        // int[]
        let old_val = ints[offset as usize];
        if old_val == expected {
            ints[offset as usize] = new_val;
            stack.borrow_mut().push_boolean(true);
        } else {
            stack.borrow_mut().push_boolean(false);
        }
    } else {
        // TODO
        panic!("todo: compareAndSwapInt!");
    }
}


// public native Object getObject(Object o, long offset);
// (Ljava/lang/Object;J)Ljava/lang/Object;
fn get_object(frame: Rc<RefCell<Frame>>) {
    let vars = frame.borrow().get_local_vars();
    let fields = unsafe { &*vars.borrow().get_ref(1).unwrap() }.get_data();
    let offset = vars.borrow().get_long(2);

    let stack = frame.borrow().get_operand_stack();
    if let DataType::Slots(anys) = fields {
        // object
        let x = anys.get_ref(offset as usize);
        stack.borrow_mut().push_ref(x);
    } else if let DataType::Array(ArrayObject::Object(objs)) = fields {
        // ref[]
        let x = objs[offset as usize].clone();
        stack.borrow_mut().push_ref(x);
    } else {
        panic!("getObject!");
    }
}

// public native void putObjectVolatile(Object var1, long var2, Object var4);
// (Ljava/lang/Object;JLjava/lang/Object;)V
fn put_object_volatile(frame: Rc<RefCell<Frame>>) {
    let vars = frame.borrow().get_local_vars();
    let fields = unsafe { &mut *vars.borrow().get_ref(1).unwrap() }.get_data_mut();
    let offset = vars.borrow().get_long(2) as usize;
    let obj = vars.borrow().get_ref(4);
    if let DataType::Slots(anys) = fields {
        // object
        anys.set_ref(offset, obj);
    } else if let DataType::Array(ArrayObject::Object(objs)) = fields {
        // ref[]
        objs[offset] = obj;
    } else {
        panic!("putObject!");
    }
}


// public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);
// (Ljava/lang/Object;JJJ)Z
fn compare_and_swap_long(frame: Rc<RefCell<Frame>>) {
    let vars = frame.borrow().get_local_vars();
    let fields = unsafe { &mut *vars.borrow().get_ref(1).unwrap() }.get_data_mut();
    let offset = vars.borrow().get_long(2);
    let expected = vars.borrow().get_long(4);
    let new_val = vars.borrow().get_long(6);

    let stack = frame.borrow().get_operand_stack();
    if let DataType::Slots(slots) = fields {
        // object
        let old_val = slots.get_long(offset as usize);
        if old_val == expected {
            slots.set_long(offset as usize, new_val);
            stack.borrow_mut().push_boolean(true);
        } else {
            stack.borrow_mut().push_boolean(false);
        }
    } else if let DataType::Array(ArrayObject::Long(longs)) = fields {
        // int[]
        let old_val = longs[offset as usize];
        if old_val == expected {
            longs[offset as usize] = new_val;
            stack.borrow_mut().push_boolean(true);
        } else {
            stack.borrow_mut().push_boolean(false);
        }
    } else {
        // TODO
        panic!("todo: compareAndSwapLong!");
    }
}

// 判断是否需要初始化一个类，通常需要使用在获取一个类的静态属性的时候(因为一个类如果没初始化，它的静态属性也不会初始化)。 此方法当且仅当ensureClassInitialized方法不生效的时候才返回false。
// public native boolean shouldBeInitialized(Class<?> var1);
// (Ljava/lang/Class;)Z
fn should_be_initialized(frame: Rc<RefCell<Frame>>) {
    // 未初始化时初始化
    let vars = frame.borrow().get_local_vars();
    // let this = vars.borrow().get_this();
    let jclass = vars.borrow().get_ref(1).unwrap();
    unsafe {
        if let Some(ExtraType::Class(class)) = { &*jclass }.get_extra() {
            // init class
            if !class.init_started() {
                // 获取到此锁可进行初始化。
                let init_lock = class.get_init_lock();
                let _lock = init_lock.lock().unwrap();
                // 再次判断，若在其他线程初始化后获取到锁则跳过初始化
                if !class.init_started() {
                    base::init_class(frame.borrow().get_thread(), class);
                }
            }
            frame.borrow().get_operand_stack().borrow_mut().push_boolean(true);
        } else {
            frame.borrow().get_operand_stack().borrow_mut().push_boolean(false);
        }
    }
}

// 该方法用于确保给定类已初始化。这通常需要与获取类的静态字段基一起使用。
// public native void ensureClassInitialized(Class<?> var1);
// (Ljava/lang/Class;)V
fn ensure_class_initialized(frame: Rc<RefCell<Frame>>) {
    // 未初始化时初始化
    let vars = frame.borrow().get_local_vars();
    // let this = vars.borrow().get_this();
    let jclass = vars.borrow().get_ref(1).unwrap();
    unsafe {
        if let Some(ExtraType::Class(class)) = { &*jclass }.get_extra() {
            // init class
            if !class.init_started() {
                // 获取到此锁可进行初始化。
                let init_lock = class.get_init_lock();
                let _lock = init_lock.lock().unwrap();
                // 再次判断，若在其他线程初始化后获取到锁则跳过初始化
                if !class.init_started() {
                    base::init_class(frame.borrow().get_thread(), class);
                }
            }
        }
    }
}



// public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);
// (Ljava/lang/Class;[B[Ljava/lang/Object;)Ljava/lang/Class;
fn define_anonymous_class(frame: Rc<RefCell<Frame>>) {
    unsafe {
        let vars = frame.borrow().get_local_vars();
        let host_class_obj = vars.borrow().get_ref(1).unwrap();
        let host_class = { &*host_class_obj }.get_extra().unwrap().get_class();
        let data = { &*vars.borrow().get_ref(2).unwrap() }.get_array().get_bytes().iter().map(|n| *n as u8).collect::<Vec<u8>>();
        let cp_patches_obj = vars.borrow().get_ref(3);
        let cp_patches = match cp_patches_obj {
            Some(obj) => Some({&*obj}.get_array().get_refs()),
            None => None
        };
        // dbg!(host_class.get_name());
        // dbg!(data);
        // dbg!(cp_patches.is_some());
        let result = ClassFile::parse(data);
        let anonymous_class = match result {
            Ok(cf) => host_class.new_anonymous_class(cf, cp_patches),
            Err(_) => panic!("java.lang.ClassFormatError")
        };
        // let frames = frame.borrow().get_thread().lock().unwrap().get_frames();
        // for fa in  frames {
        //     let method = fa.borrow().get_method();
        //     let class_name = method.get_class().get_name();
        //     let line_num = method.get_line_number(fa.borrow().get_next_pc() as i32);
        //     println!(">> line:{} pc:{} {}.{}{} ", line_num, fa.borrow().get_next_pc(), class_name, method.get_name(), method.get_descriptor());
        //     // let s = format!(">> line:{} pc:{} {}.{}{} \n", line_num, frame.borrow().get_next_pc(), class_name, method.get_name(), method.get_descriptor());
        //     // file.write_all(s.as_bytes()).unwrap();
        // }
        // println!("");
        // dbg!(anonymous_class.get_name());
        let jl_class_class = { &*host_class_obj }.get_class();
        let mut j_class = Class::new_object(jl_class_class.clone());
        j_class.set_extra(Some(ExtraType::Class(anonymous_class.clone())));
        // 231027 Class unsafe 此时处于创建过程中，通过unsafe修改内部值是安全的。
        unsafe { &mut *(Arc::as_ptr(&anonymous_class) as *mut Class) }.set_j_class(Some(Box::into_raw(Box::new(j_class))));
        
        frame.borrow().get_operand_stack().borrow_mut().push_ref(anonymous_class.get_j_class());
    }
    
}



// 将引用值存储到给定的 Java 变量中。
// 除非存储的引用 x 为 null 或与字段类型匹配，否则结果是未定义的。
// 如果引用 o 为非 null，则会更新该对象的汽车标记或其他存储障碍（如果 VM 需要它们）。
// public native void putObject(Object o, long offset, Object x);
// (Ljava/lang/Object;JLjava/lang/Object;)V
fn put_object(frame: Rc<RefCell<Frame>>) {
	let vars = frame.borrow().get_local_vars();
	// vars.GetRef(0) // this
    let o = vars.borrow().get_ref(1);
    let offset = vars.borrow().get_long(2) as usize;
    let x = vars.borrow().get_ref(4);
    if let Some(o) = o {
        unsafe {
            match { &mut *o }.get_data_mut() {
                DataType::Slots(slots) => {
                    // 返回class
                    if let Some(x) = x {
                        let class = {&*o}.get_class();
                        let fields = class.get_fields(false);
                        let field = fields.into_iter().find(|f| f.get_slot_id() == offset).unwrap();
                        let x_class = {&*x}.get_class();
                        let f_class = field.get_class();
                        if Arc::ptr_eq(&x_class, &f_class) || x_class.is_sub_class_of(&f_class) {
                            slots.set_ref(offset, Some(x));
                        }
                    } else {
                        slots.set_ref(offset, None);
                    }
                },
                DataType::Array(ArrayObject::Object(objs)) => {
                    objs[offset] = x;
                },
                _ => panic!("putObject!")
            }
        }
    }
}

// unpark、park 参考：https://blog.csdn.net/a7980718/article/details/83661613
// 取消阻止在暂 留时阻塞的给定线程，或者，如果未阻塞，则导致后续 的暂存 调用不阻塞。
// 注意：此操作是“不安全的”，因为调用方必须以某种方式确保线程未被破坏。当从 Java 调用时，
// 通常不需要任何特殊的东西来确保这一点（其中通常会有对线程的实时引用），但当从本机代码调用时，这几乎是自动的。
// 参数：thread – 要取消停放的线程。
// public native void unpark(Object thread);
// (Ljava/lang/Object;)V
fn unpark(frame: Rc<RefCell<Frame>>) {
    let vars = frame.borrow().get_local_vars();
    let thread = vars.borrow().get_ref(1).expect("java.lang.NullPointerException");
    let key = thread as usize;
    let threads = THREADS.lock().unwrap();
    let join_handle = threads.get(&key);
    if let Some(jh) = join_handle {
        jh.thread().unpark();
    } else {
        UNPARK.lock().unwrap().insert(key);
    }
    drop(threads);
}

// 阻止当前线程，在发生平衡取消寄存、或已发生平衡取消寄存、或线程中断时返回，
// 或者，如果不是绝对且时间不为零，则给定时间纳秒已过，或者如果绝对，则自纪元过去以来的给定截止时间（以毫秒为单位），
// 或虚假（即无“理由”返回）。注意：此操作仅在 Unsafe 类中，因为 unpark 是，因此将其放在其他地方会很奇怪。
// public native void park(boolean isAbsolute, long time);
// (ZJ)V
fn park(frame: Rc<RefCell<Frame>>) {
    let vars = frame.borrow().get_local_vars();
    let is_absolute = vars.borrow().get_boolean(1);
    let time = vars.borrow().get_long(2);

    let thread = frame.borrow().get_thread();
    let j_thread = thread.lock().unwrap().get_j_thread();
    if let Some(j_thread) = j_thread {
        let key = j_thread as usize;
        if UNPARK.lock().unwrap().remove(&key) {
            return;
        }
    }
    // 在调用park()之前调用了unpark或者interrupt则park直接返回，不会挂起。
    if is_absolute { // ms
        if time > 0 {
            std::thread::park_timeout(Duration::from_millis(time as u64));
        }
    } else { // ns
        if time == 0 {
            std::thread::park();
        } else if time > 0 {
            std::thread::park_timeout(Duration::from_nanos(time as u64));
        }
    }
   
}